#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */ 
#endregion

using Adoor.Expression;
using Adoor.Expression.AST;
using System.Data;
using System.Collections.Generic;
using System.Text;


namespace Adoor.Relational.SqlDriver
{
	public class TransactSqlDriver: CommonSqlDriver
	{
		public TransactSqlDriver(int paramCount): base(paramCount)
		{
		}

		public TransactSqlDriver(): this(0)
		{
		}

		protected override CommonSqlDriver Clone()
		{
			return new TransactSqlDriver(this.paramCount);
		}

		public static string EscapeTsql(string name)
		{
			return "[" + name + "]";
		}

		protected override string Escape(string name)
		{
			return EscapeTsql(name);
		}

		protected override string GenerateExpression(ExpressionNode en)
		{
			return TsqlExpressionStringizer.Evaluate(this, en);
		}

		class TsqlExpressionStringizer: CommonExpressionStringizer
		{
			public static string Evaluate(TransactSqlDriver driver, ExpressionNode n)
			{
				TsqlExpressionStringizer es = new TsqlExpressionStringizer(driver);
				n.Accept(es);
				return es.Result.ToString();
			}

			protected TsqlExpressionStringizer(TransactSqlDriver driver)
				: base(driver)
			{
			}

			public override void VisitName(ExpressionName n)
			{
				if (n.Qualifier != "") 
				{
					this.Result.Append(TransactSqlDriver.EscapeTsql(n.Qualifier));
					this.Result.Append('.');
					this.Result.Append(n.Value);
					return;
				}
				this.Result.Append(n.Value);				
			}

			public override void VisitFunction(ExpressionFunction f)
			{
				if(f.Name.ToLower() == "concat")
					VisitFunctionConcat(f);
				else if(f.Name.ToLower() == "convert")
					VisitFunctionConvert(f);
				else if (f.Name.ToLower() == "datepart")
					VisitFunctionDatepart(f);
				else if (f.Name.ToLower() == "datediff")
					VisitFunctionDatediff(f);
				else if (f.Name.ToLower() == "dateadd")
					VisitFunctionDateadd(f);
				else
					base.VisitFunction (f);
			}

			public virtual void VisitFunctionConcat(ExpressionFunction f)
			{
				bool needPlus = false;

				this.Result.Append("(");
				foreach( ExpressionNode en in f.Parameters)
				{
					if(needPlus)
						this.Result.Append("+");

					this.Result.Append(" CAST( ");
					en.Accept(this);
					this.Result.Append(" as varchar ) ");

					needPlus = true;
				}
				this.Result.Append(")");
			}

			public virtual void VisitFunctionConvert(ExpressionFunction f)
			{
				this.Result.Append(" ");
				this.Result.Append(f.Name);
				this.Result.Append("(");
			
				ExpressionPrimary prim = (ExpressionPrimary) f.Parameters[0];
			
				prim.Value = prim.Value.Replace("'", "");
				prim.Accept(this);

				for(int i = 1; i < f.Parameters.Length; i++)
				{
					this.Result.Append(",");
					f.Parameters[i].Accept(this);
				}
				this.Result.Append(")");
			}
			public virtual void VisitFunctionDatepart(ExpressionFunction f)
			{
				this.Result.Append(" ");
				this.Result.Append(f.Name);
				this.Result.Append("(");
			
				ExpressionPrimary prim = (ExpressionPrimary) f.Parameters[0];
			
				prim.Value = prim.Value.Replace("'", "");
				prim.Accept(this);

				for(int i = 1; i < f.Parameters.Length; i++)
				{
					this.Result.Append(",");
					f.Parameters[i].Accept(this);
				}
				this.Result.Append(")");
			}
			public virtual void VisitFunctionDatediff(ExpressionFunction f)
			{
				this.Result.Append(" ");
				this.Result.Append(f.Name);
				this.Result.Append("(");
			
				ExpressionPrimary prim = (ExpressionPrimary) f.Parameters[0];
			
				prim.Value = prim.Value.Replace("'", "");
				prim.Accept(this);

				for(int i = 1; i < f.Parameters.Length; i++)
				{
					this.Result.Append(",");
					f.Parameters[i].Accept(this);
				}
				this.Result.Append(")");
			}
			public virtual void VisitFunctionDateadd(ExpressionFunction f)
			{
				this.Result.Append(" ");
				this.Result.Append(f.Name);
				this.Result.Append("(");
			
				ExpressionPrimary prim = (ExpressionPrimary) f.Parameters[0];
			
				prim.Value = prim.Value.Replace("'", "");
				prim.Accept(this);

				for(int i = 1; i < f.Parameters.Length; i++)
				{
					this.Result.Append(",");
					f.Parameters[i].Accept(this);
				}
				this.Result.Append(")");
			}
		}

//		public override string DefaultSelectIsolationLevelQuery
//		{
//			get
//			{
//				return "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
//			}
//		}

        public override string GetUpdateCommand(DataTable table, bool updateWhereAll, bool refreshDataFromDatabase, ICollection<DataColumn> dataColumns, LockMode lockMode)
        {
            StringBuilder result = new StringBuilder("UPDATE ");
            result.Append(table.TableName);
            result.Append(" SET ");
            int i = 0;
            foreach (DataColumn dc in dataColumns)
            {
                if (dc.ReadOnly)
                    continue;
                if (dc.Expression != "" || IsInPrimaryKey(dc, table))
                    continue;
                if (i != 0)
                    result.Append(", ");

                //result.Append(Escape(dc.ColumnName));
                result.Append(dc.ColumnName);
                result.AppendFormat(" = {{{0}}}", i++);
            }

            result.AppendFormat(" , ModifiePar = '{0}' , ModifieLe = '{1}' ",
                System.Threading.Thread.CurrentPrincipal.Identity.Name.Trim().Replace("'", "''"),
                System.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));

            result.Append(" WHERE ");
            bool comma = false;
            int idxStartPkColumns = i;
            foreach (DataColumn dc in table.PrimaryKey)
            {
                if (dc.ReadOnly)
                    continue;
                if (comma)
                    result.Append(" AND ");
                comma = true;

                //result.Append(Escape(dc.ColumnName));
                result.Append(dc.ColumnName);
                result.AppendFormat(" = {{{0}}}", i++);
            }
            if (updateWhereAll)
            {
                // TODO
            }

            if (refreshDataFromDatabase)
                result.Append(this.GetRefreshDataStatement(table, idxStartPkColumns, dataColumns));

            return result.ToString();
        }


        //Check this work with other database
        protected virtual string GetRefreshDataStatement(DataTable table, int idxStartPkCollumns, ICollection<DataColumn> dataColumns)
        {
            StringBuilder result = new StringBuilder("; ");
            result.Append(this.GetSelectCommand(table, dataColumns));
            result.Append(" where ");
            int i = idxStartPkCollumns;
            foreach (DataColumn dc in table.PrimaryKey)
            {
                if (i != idxStartPkCollumns)
                    result.Append(" and ");
                result.Append(dc.ColumnName);
                result.Append(" = {" + i.ToString() + "}");
                i++;
            }
            return result.ToString();
        }

        public override string GetInsertCommand(DataTable table, bool refreshDataFromDatabase, ICollection<DataColumn> dataColumns, LockMode lockMode)
        {
            StringBuilder result = new StringBuilder("INSERT INTO ");
            //result.Append(Escape(table.TableName));
            result.Append(table.TableName);
            result.Append(" (");
            int i = 0;
            foreach (DataColumn dc in dataColumns)
            {
                if (dc.ReadOnly)
                    continue;
                if (dc.Expression != "")
                    continue;
                if (i != 0)
                    result.Append(", ");
                ++i;
                //result.Append(Escape(dc.ColumnName));
                result.Append(dc.ColumnName);
            }
            result.Append(" , ModifiePar , ModifieLe ");
            result.Append(") VALUES (");
            i = 0;
            foreach (DataColumn dc in dataColumns)
            {
                if (dc.ReadOnly)
                    continue;
                if (dc.Expression != "")
                    continue;
                if (i != 0)
                    result.Append(", ");
                result.Append("{" + (i++).ToString() + "}");
            }
            result.AppendFormat(", '{0}', '{1}' ",
                System.Threading.Thread.CurrentPrincipal.Identity.Name.Replace("'", "''"),
                System.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));
            result.Append(")");

            if (refreshDataFromDatabase)
                result.Append(this.GetRefreshDataStatement(table, 0, dataColumns));

            return result.ToString();
        }

        public override string GetDeleteCommand(DataTable table, bool updateWhereAll, ICollection<DataColumn> dataColumns, LockMode lockMode)
        {
            StringBuilder result = new StringBuilder("DELETE FROM ");
            //result.Append(Escape(table.TableName));
            result.Append(table.TableName);
            result.Append(" WHERE ");
            int i = 0;
            foreach (DataColumn dc in table.PrimaryKey)
            {
                if (i != 0)
                    result.Append(" AND ");
                //result.Append(Escape(dc.ColumnName));
                result.Append(dc.ColumnName);
                result.Append(" = {" + (i++).ToString() + "}");
            }
            //			if (updateWhereAll)
            //				foreach (DataColumn dc in dataColumns)
            //				{
            //					// TODO
            //				}
            return result.ToString();
        }

	}
}
